/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.model.core;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>Layout based on Tiles definition. </p>
 * <p>Layout is a {@link Localizable} that is to be used as a template for pages.
 * Layout may have tiles definition for content pages, that describes actual 'layout'
 * of information on pages that will be based on this layout. Also, Layout has
 * {@link ContentField}s and menu (built from {@link MenuItem}s); they will be
 * inherited by derived pages.
 * </p>
 * <p>Layout may have {@link TileContainer}s assigned to it. They give user
 * possibility to change contents of some lists which are attributes of
 * Tiles definitions in run-time. 
 * </p>
 * <p>Layout may be virtual. Virtual layout is based on virtual definitions
 * which exist only in run-time. These definitions extend some other definitions
 * (possibly virtual too). This allows to have many layouts that behave so as
 * they are based on same definition - useful if user needs to have multiple
 * layouts which distinct only on menus and tile containers.
 * </p>
 * <p><a href="Layout.java.html"><i>View Source</i></a>
 * </p>
 * <p/>
 *
 * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.32 $ $Date: 2007/07/24 09:28:58 $
 * @see ContentPage
 * @see ContentField
 * @see Localizable
 * @struts.form include-all="false" extends="BaseForm"
 * @hibernate.joined-subclass table="`al_core_layout`" lazy="false"
 * @hibernate.joined-subclass-key column="`localizable_id`"
 */
public class Layout extends Localizable {

    //~ Instance variables

    /**
     * Name of tiles definition that correspond to this layout
     */
    protected String definition;
    /**
     * Name of this layout
     */
    protected String name;
    /**
     * Name of additional tiles definition for content page
     */
    protected String cpDefinition;
    /**
     * Name of tiles definition which is parent of virtual definition
     * that correspond to this layout
     */
    protected String definitionParent;
    /**
     * Name of parent of virtual additional tiles definition for content page
     */
    protected String cpDefinitionParent;
    /**
     * Whether layout is virtual (so its definition and cpDefinition are
     * virtual and inherit definitionParent and cpDefinitionParent)
     */
    protected Boolean virtual;
    /**
     * List of content pages which are based on this layout 
     */
    protected List contentPages = new ArrayList();
    /**
     * List of tile containers which belong to this layout
     */
    protected List tileContainers = new ArrayList();

    //~ Methods

    /**
     * Returns the Tiles definition name
     *
     * @return definition name
     * @struts.form-field
     * @struts.validator type="required"
     * @struts.validator-args arg0resource="core.layout.form.definition"
     * @hibernate.property
     * @hibernate.column name="`definition`" not-null="true" unique="true" length="252"
     */
    public String getDefinition() {
        return definition;
    }

    /**
     * Sets the Tiles definition name
     *
     * @param definition definition name to set
     */
    public void setDefinition(String definition) {
        this.definition = definition;
    }


    /**
     * Gets the content page definition name
     *
     * @return content page definition name
     * @struts.form-field
     * @hibernate.property
     * @hibernate.column name="`cpDefinition`" not-null="false" unique="false" length="252"
     */
    public String getCpDefinition() {
        return cpDefinition;
    }

    /**
     * Sets the content page definition name
     *
     * @param cpDefinition content page definition name
     */
    public void setCpDefinition(String cpDefinition) {
        this.cpDefinition = cpDefinition;
    }


    /**
     * Returns the name of the layout.
     *
     * @return name
     * @struts.form-field
     * @struts.validator type="required"
     * @struts.validator-args arg0resource="core.layout.form.name"
     * @struts.validator type="mask" msgkey="core.commons.errors.layoutName"
     * @struts.validator-var name="mask" value="^[^&lt;&gt;&amp;\042]+$"
     * @hibernate.property
     * @hibernate.column name="`name`" not-null="true" unique="true" length="252"
     */
    public String getName() {
        return name;
    }

    /**
     * Sets name of the layout
     *
     * @param name name of the layout
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Returns name of tiles definition which is parent of virtual definition
     * that correspond to this layout.
     *
     * @return definition parent name
     * @hibernate.property
     * @hibernate.column name="`definition_parent`" not-null="false" unique="false" length="252"
     */
    public String getDefinitionParent() {
        return definitionParent;
    }

    /**
     * Sets name of tiles definition which is parent of virtual definition
     * that correspond to this layout.
     *
     * @param definitionParent definition parent name
     */
    public void setDefinitionParent(String definitionParent) {
        this.definitionParent = definitionParent;
    }

    /**
     * Returns name of parent of virtual additional tiles definition for
     * content page.
     *
     * @return content page definition parent name
     * @hibernate.property
     * @hibernate.column name="`cpDefinition_parent`" not-null="false" unique="false" length="252"
     */
    public String getCpDefinitionParent() {
        return cpDefinitionParent;
    }

    /**
     * Sets name of parent of virtual additional tiles definition for
     * content page.
     *
     * @param cpDefinitionParent CP definition parent name
     */
    public void setCpDefinitionParent(String cpDefinitionParent) {
        this.cpDefinitionParent = cpDefinitionParent;
    }

    /**
     * Returns whether layout is virtual (so its definition and cpDefinition are
     * virtual and inherit definitionParent and cpDefinitionParent) or not.
     * 
     * @return virtual or not
     * @struts.form-field
     * @hibernate.property column="`virtual`" not-null="true" type="true_false"
     */
    public Boolean getVirtual() {
        return virtual;
    }

    /**
     * Sets whether layout is virtual.
     *
     * @param virtual value to set
     * @see #virtual
     */
    public void setVirtual(Boolean virtual) {
        this.virtual = virtual;
    }

    /**
     * Returns list of content pages which are based on this layout
     *
     * @return list of content pages
     * @hibernate.bag name="contentPages" inverse="true" lazy="true" cascade="delete" outer-join="false"
     * @hibernate.collection-key column="`layout_id`"
     * @hibernate.collection-one-to-many class="com.blandware.atleap.model.core.ContentPage"
     * @hibernate.cache usage="read-write"
     */
    public List getContentPages() {
        return contentPages;
    }

    /**
     * Sets list of content pages which are based on this layout
     *
     * @param contentPages list of content pages
     */
    public void setContentPages(List contentPages) {
        this.contentPages = contentPages;
    }

    /**
     * Adds a content page to the list of pages which are based on this layout
     *
     * @param contentPage the content page to be added
     */
    public void addContentPage(ContentPage contentPage) {
        contentPage.setLayout(this);
        contentPages.add(contentPage);
    }

    /**
     * Updates content page for this layout: page layout is other then this,
     * content page is added to this layout pages list and removed from pages
     * list of initial layout.
     *
     * @param contentPage the page to be updated
     * @return old ContentPage object (the one that belonged to initial layout)
     */
    public ContentPage updateContentPage(ContentPage contentPage) {
        ContentPage oldContentPage = null;
        if ( !contentPage.getLayout().equals(this) ) {
            //int index = contentPage.getLayout().contentPages.indexOf(contentPage);
            // The above line is replaced with following lines because
            // business key for ContentPage (uri) may be changed along with
            // page layout, in such case index will be -1, so we're searching
            // page by its ID, not by equals() method.

            int index = -1;
            for (int i = 0; i < contentPage.getLayout().contentPages.size(); i++) {
                ContentPage curPage = (ContentPage) contentPage.getLayout().contentPages.get(i);
                if (contentPage.getId().equals(curPage.getId())) {
                    index = i;
                    break;
                }
            }

            if (index != -1)
                oldContentPage = (ContentPage)contentPage.getLayout().contentPages.remove(index);
            contentPages.add(contentPage);
            contentPage.setLayout(this);
        }
        return oldContentPage;
    }

    /**
     * Returns list of tile containers which belong to this layout.
     *
     * @return list of tile containers
     * @hibernate.bag name="tileContainers" inverse="true" lazy="true" cascade="delete" outer-join="false"
     * @hibernate.collection-key column="`layout_id`"
     * @hibernate.collection-one-to-many class="com.blandware.atleap.model.core.TileContainer"
     * @hibernate.cache usage="read-write"
     */
    public List getTileContainers() {
        return tileContainers;
    }

    /**
     * Sets list of tile containers which belong to this layout.
     *
     * @param tileContainers list of tile containers
     */
    public void setTileContainers(List tileContainers) {
        this.tileContainers = tileContainers;
    }

    /**
     * Removes a content page from the list of pages which are based on this layout
     *
     * @param contentPage the content page to be removed
     */
    public void removeContentPage(ContentPage contentPage) {
        contentPages.remove(contentPage);
    }

    public boolean equals(Object o) {
        if ( this == o ) {
            return true;
        }
        if ( !(o instanceof Layout) ) {
            return false;
        }

        final Layout layout = (Layout) o;

        if ( definition != null ? !definition.equals(layout.definition) : layout.definition != null ) {
            return false;
        }

        return true;
    }

    public int hashCode() {
        return (definition != null ? definition.hashCode() : 0);
    }

}
